package com.niiwoo.civet.account.service.local.bill;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.account.dao.entity.*;
import com.niiwoo.civet.account.dao.mapper.BankTypeMapper;
import com.niiwoo.civet.account.dao.mapper.UserBillBaseMapperExt;
import com.niiwoo.civet.account.dto.bill.*;
import com.niiwoo.civet.account.dto.request.BillIntelligenceInvestForApplyRequestDTO;
import com.niiwoo.civet.account.dto.request.HandAddBillAndChangeBalanceDTO;
import com.niiwoo.civet.account.enums.*;
import com.niiwoo.civet.account.service.local.AccountBalanceLocalService;
import com.niiwoo.civet.account.service.local.AccountService;
import com.niiwoo.civet.account.service.local.BankCardService;
import com.niiwoo.civet.account.service.local.trade.OrderSuccessAccountingLocalService;
import com.niiwoo.civet.trade.dto.common.ProjectDTO;
import com.niiwoo.civet.trade.dto.response.intelligenceInvest.ProjectPackageResponseDTO;
import com.niiwoo.civet.trade.service.intelligenceInvest.IntelligenceInvestDubboService;
import com.niiwoo.civet.trade.service.project.ProjectDubboService;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.Validator;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 账单处理Facade
 *
 * @author xiaoyingping
 * @create 2017-11-22 10:42
 **/
@Service
@Slf4j
public class BillFacade {

    @Autowired
    private UserBillBaseMapperExt userBillBaseMapperExt;

    @Autowired
    private BillBaseService billBaseService;
    @Autowired
    private NiiwooBillService niiwooBillService;
    @Autowired
    private NiiwooBillPlateformService niiwooBillPlateformService;
    @Autowired
    private NiiwooBillGuaranteeService niiwooBillGuaranteeService;
    @Autowired
    private AccountBalanceLocalService accountBalanceLocalService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private BankCardService bankCardService;
    @Autowired
    private BankTypeMapper bankTypeMapper;
    @Autowired
    private OrderSuccessAccountingLocalService orderSuccessAccountingLocalService;
    @Autowired
    private Validator springValidator;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Reference(version = "1.0.0")
    private IntelligenceInvestDubboService intelligenceInvestDubboService;
    @Reference(version = "1.0.0")
    private ProjectDubboService projectDubboService;


    /**
     * 批量：变更账户余额 + 记账
     *
     * @param addBillAndChangeBalanceDTOList
     */
    @Transactional
    public void batchAddBillAndChangeBalance(List<AddBillAndChangeBalanceDTO> addBillAndChangeBalanceDTOList) {
        log.info("batchAddBillAndChangeBalance start:{}", JSON.toJSONString(addBillAndChangeBalanceDTOList));
        for (AddBillAndChangeBalanceDTO addBillAndChangeBalanceDTO : addBillAndChangeBalanceDTOList) {
            this.addBillAndChangeBalance(addBillAndChangeBalanceDTO);
        }
    }


    /**
     * 变更账户余额 + 记账
     *
     * @param billDTO
     * @return
     */
    @Transactional
    public Long addBillAndChangeBalance(AddBillAndChangeBalanceDTO billDTO) {
        log.info("addBillAndChangeBalance start:{}", JSON.toJSONString(billDTO));
        //校验参数
        verifyParamAddBillAndChangeBalance(billDTO);
        //调用通用记账方法
        return this.commonAddBillAndChangeBalance(billDTO);
    }

    /**
     * 手工变更账户余额 + 记账
     *
     * @param handAddBillAndChangeBalanceDTO
     * @return
     */
    @Transactional
    public Long handAddBillAndChangeBalance(HandAddBillAndChangeBalanceDTO handAddBillAndChangeBalanceDTO) {
        log.info("handAddBillAndChangeBalance start:{}", JSON.toJSONString(handAddBillAndChangeBalanceDTO));
        //校验参数
        verifyParamHandAddBillAndChangeBalance(handAddBillAndChangeBalanceDTO);

        //组装记账对象
        AddBillDetailDTO addBillDetailDTO = new AddBillDetailDTO();
        addBillDetailDTO.setAmount(handAddBillAndChangeBalanceDTO.getAmount());
        addBillDetailDTO.setBillSecondSubjectEnum(handAddBillAndChangeBalanceDTO.getBillSecondSubjectEnum());
        addBillDetailDTO.setRelationAccountId(handAddBillAndChangeBalanceDTO.getRelationAccountId());

        AddBillAndChangeBalanceDTO billDTO = new AddBillAndChangeBalanceDTO();
        billDTO.setAccountId(handAddBillAndChangeBalanceDTO.getAccountId());
        billDTO.setAmount(handAddBillAndChangeBalanceDTO.getAmount());
        billDTO.setInoutType(handAddBillAndChangeBalanceDTO.getInoutType());
        billDTO.setOrderId(-1L);
        billDTO.setOrderType(OrderTypeEnum.NONE);
        billDTO.setStatus(handAddBillAndChangeBalanceDTO.getStatus());
        billDTO.setChannelCode(handAddBillAndChangeBalanceDTO.getChannelCode());
        billDTO.setRelationAccountId(handAddBillAndChangeBalanceDTO.getRelationAccountId());
        billDTO.setRemark(handAddBillAndChangeBalanceDTO.getRemark());
        billDTO.setIsVisible(handAddBillAndChangeBalanceDTO.getIsVisible());
        billDTO.setAddBillDetailDTOList(Arrays.asList(addBillDetailDTO));

        //调用通用记账方法
        return this.commonAddBillAndChangeBalance(billDTO);
    }

    /**
     * 修改账单状态为【成功】
     *
     * @param userId
     * @param orderId
     */
    @Transactional
    public void successBillByOrderId(String userId, String orderId, OrderTypeEnum orderTypeEnum) {
        Assert.hasLength(userId, "userId不能为空");
        Assert.notNull(orderId, "orderId不能为空");
        log.info("successBillByOrderId start, userId:{}, orderId:{}, orderTypeEnum:{}", userId, orderId, orderTypeEnum);

        List<BillBase> billBaseList = billBaseService.queryByOrderIdAndTypeForUpdate(orderId, orderTypeEnum);
        log.info("通过订单ID查询出的账单是:{}", JSON.toJSONString(billBaseList));

        BillStatusEnum newStatus = BillStatusEnum.SUCCESS;
        for (BillBase billBase : billBaseList) {
            if (billBase.getStatus() == newStatus.getValue()) {
                log.info("当前状态已是{}，不需要再修改", newStatus.getValue());
                continue;
            }

            //修改账单状态
            billBaseService.updateBillBaseStatus(billBase, newStatus);

            //修改账单流水明细状态
            BillUserTypeEnum billUserTypeEnum = BillUserTypeEnum.getEnumByCode(billBase.getBillType());
            if (BillUserTypeEnum.GENERAL == billUserTypeEnum) {
                //普通用户
                log.info("successBillByOrderId用户");
                niiwooBillService.updateBillStatusByOrderId(userId, orderId, orderTypeEnum, newStatus);
            } else if (BillUserTypeEnum.GUARANTEE_AGENCY == billUserTypeEnum || BillUserTypeEnum.COOPERATE_AGENCY == billUserTypeEnum) {
                //担保机构|合作机构
                log.info("successBillByOrderId机构");
                niiwooBillGuaranteeService.updateBillStatusByOrderId(orderId, orderTypeEnum, newStatus);
            } else if (BillUserTypeEnum.PLATFORM == billUserTypeEnum) {
                //平台
                log.info("successBillByOrderId平台");
                niiwooBillPlateformService.updateBillStatusByOrderId(orderId, orderTypeEnum, newStatus);
            } else {
                throw new RuntimeException("billUserType不能处理:{}" + billUserTypeEnum);
            }
        }
    }


    /**
     * 关闭账单 + 新增回滚账单 + 变动账户余额
     *
     * @param orderId
     * @param orderTypeEnum
     */
    @Transactional
    public void closeAndRevertBill(String orderId, OrderTypeEnum orderTypeEnum, String remark2) {
        Assert.notNull(orderId, "orderId不能为空");
        Assert.notNull(orderTypeEnum, "orderTypeEnum不能为空");
        log.info("closeAndRevertBill start, orderId:{}, orderTypeEnum:{}, remark2:{}", orderId, orderTypeEnum.getCode(), remark2);

        Date nowDate = new Date();
        BillStatusEnum closeStaus = BillStatusEnum.CLOSE;
        List<BillBase> billBaseList = billBaseService.queryByOrderIdAndTypeForUpdate(orderId, orderTypeEnum);
        for (BillBase billBase : billBaseList) {
            if (BillStatusEnum.CLOSE.getValue() == billBase.getStatus()) {
                log.warn("账单目前已是关闭状态，无需操作:{}", billBase.getId());
                return;
            }

            //关闭旧账单Base
            billBaseService.updateBillBaseStatus(billBase, closeStaus);
            //新增回退的账单Base
            BillBase reverseBillBase = new BillBase();
            BeanUtils.copyProperties(billBase, reverseBillBase);
            reverseBillBase.setStatus(closeStaus.getValue());
            reverseBillBase.setRemark("");
            InoutTypeEnum reverseInoutType = InoutTypeEnum.getReverse(billBase.getInoutType());
            reverseBillBase.setInoutType(reverseInoutType.getType());
            if (InoutTypeEnum.IN_TYPE == reverseInoutType) {
                reverseBillBase.setBalanceAmount(accountBalanceLocalService.increAccountAmount(billBase.getAccountId(), billBase.getAmount()));
            } else {
                reverseBillBase.setBalanceAmount(accountBalanceLocalService.decreAccountAmount(billBase.getAccountId(), billBase.getAmount()));
            }
            reverseBillBase.setBackSourceBillId(billBase.getId());
            billBaseService.addBillBase(reverseBillBase);

            //普通用户
            if (BillUserTypeEnum.GENERAL.getCode().equals(billBase.getBillType())) {
                log.info("closeAndRevertBill用户");
                //关闭旧账单明细
                niiwooBillService.updateBillStatusByOrderId(billBase.getUserId(), orderId, orderTypeEnum, closeStaus);
                //新增回退账单明细
                List<NiiwooBill> niiwooBills = niiwooBillService.getByOrderIdAndType(billBase.getUserId(), orderId, orderTypeEnum);
                niiwooBills.forEach(oldBill -> {
                    NiiwooBill reverseBill = new NiiwooBill();
                    BeanUtils.copyProperties(oldBill, reverseBill);
                    reverseBill.setSecondSortCode(this.getReverseBillSecondSubject(oldBill.getSecondSortCode()).getCode());
                    reverseBill.setBackTime(nowDate);
                    reverseBill.setRemark2(remark2);
                    niiwooBillService.addNiiwooBill(reverseBillBase, reverseBill);
                });
            }

            //担保机构|合作机构
            if (BillUserTypeEnum.GUARANTEE_AGENCY.getCode().equals(billBase.getBillType()) || BillUserTypeEnum.COOPERATE_AGENCY.getCode().equals(billBase.getBillType())) {
                log.info("closeAndRevertBill机构");
                //关闭旧账单明细
                niiwooBillGuaranteeService.updateBillStatusByOrderId(orderId, orderTypeEnum, closeStaus);
                //新增回退账单明细
                List<NiiwooBillGuarantee> niiwooBillGuaranteeList = niiwooBillGuaranteeService.getByOrderIdAndType(billBase.getUserId(), orderId, orderTypeEnum);
                niiwooBillGuaranteeList.forEach(oldBill -> {
                    NiiwooBillGuarantee reverseBill = new NiiwooBillGuarantee();
                    BeanUtils.copyProperties(oldBill, reverseBill);
                    reverseBill.setSecondSortCode(getReverseBillSecondSubject(oldBill.getSecondSortCode()).getCode());
                    niiwooBillGuaranteeService.addNiiwooBillGuarantee(reverseBillBase, reverseBill);
                });
            }

            //平台
            if (BillUserTypeEnum.PLATFORM.getCode().equals(billBase.getBillType())) {
                log.info("closeAndRevertBill平台");
                //关闭旧账单明细
                niiwooBillPlateformService.updateBillStatusByOrderId(orderId, orderTypeEnum, closeStaus);
                //新增回退账单明细
                List<NiiwooBillPlatform> niiwooBillPlatformList = niiwooBillPlateformService.getByOrderIdAndType(orderId, orderTypeEnum);
                niiwooBillPlatformList.forEach(oldBill -> {
                    NiiwooBillPlatform reverseBill = new NiiwooBillPlatform();
                    BeanUtils.copyProperties(oldBill, reverseBill);
                    reverseBill.setSecondSortCode(getReverseBillSecondSubject(oldBill.getSecondSortCode()).getCode());
                    niiwooBillPlateformService.addNiiwooBillPlatform(reverseBillBase, reverseBill);
                });
            }
        }
    }


    /**
     * ============================================智能投记账方法区start==============================================
     */

    /**
     * 智能投记帐：投资申请
     */
    @Transactional
    public Long billIntelligenceInvestForApply(BillIntelligenceInvestForApplyRequestDTO request) {
        //幂等处理：新增债转记账成功记录
        OrderSuccessAccounting orderSuccessAccounting = orderSuccessAccountingLocalService.queryOrderSuccessAccountingByOrderIdAndOrderType(request.getOrderId(), OrderTypeEnum.ZNT_INVEST);
        if(Objects.nonNull(orderSuccessAccounting)){
            log.info("债智能投投资申请记录已存在，直接返回");
            return orderSuccessAccounting.getId();
        }
        Long orderSuccessAccountingId = orderSuccessAccountingLocalService.insertOrderSuccessAccounting(request.getOrderId(), OrderTypeEnum.ZNT_INVEST);

        //扣减账户余额
        Long accountId = request.getAccountId();
        AccountBase accountBase = accountService.loadAccountByIdFromMaster(accountId);
        BigDecimal amount = request.getAmount().abs();
        BigDecimal balanceAmount = accountBalanceLocalService.decreAccountAmount(accountId, amount);

        //记账(只新增billBase)
        BillBase billBase = new BillBase();
        billBase.setAccountId(accountId);
        billBase.setUserId(accountBase.getUserId());
        billBase.setOrgId(accountBase.getOrgId());
        billBase.setRequestNo(String.valueOf(snowflakeIdWorker.nextId()));//只在本地有，存管没有
        billBase.setAmount(amount);
        billBase.setInoutType(InoutTypeEnum.OUT_TYPE.getType());
        billBase.setBillType(transToBillUserType(accountBase.getAccountRole()).getCode());
        billBase.setOrderId(String.valueOf(request.getOrderId()));
        billBase.setOrderType(OrderTypeEnum.ZNT_INVEST.getCode());
        billBase.setStatus(BillStatusEnum.HANDLING.getValue());
        billBase.setProjectPackageId(request.getProjectPackageId());
        billBase.setInvestId(request.getOrderId());
        billBase.setBalanceAmount(balanceAmount);
        billBase.setIsVisible(BillIsVisibleEnum.SHOW.getValue());
        billBase.setChannelCode(com.niiwoo.civet.base.enums.ChannelCodeEnum.APP.getChanelCode());
        billBaseService.addBillBase(billBase);

        return orderSuccessAccountingId;
    }

    /**
     * 智能投记帐：匹配底层标的
     * @param request
     */
    @Transactional
    public void billIntelligenceInvestForMahch(BillIntelligenceInvestForMahchRequest request) {
        //校验参数
        log.info("billIntelligenceInvestForMahch start, request:{}", JSON.toJSONString(request));
        Errors errors = new BindException(request,"请求参数");
        springValidator.validate(request, errors);
        if(errors.hasErrors()){
            FieldError fieldError = errors.getFieldErrors().get(0);
            throw new IllegalArgumentException(fieldError.getField().concat(fieldError.getDefaultMessage()));
        }

        //查询智能投时的账单Base
        String userId = request.getUserId();
        String intelligenceInvestOrderId = request.getPackageOrderId();
        BillBase intelligenceInvestBillBase = billBaseService.getUserBillBaseByOrderId(userId, intelligenceInvestOrderId);

        //新增BillFlow
        AccountBase accountBase = accountService.loadAccountByIdFromMaster(intelligenceInvestBillBase.getAccountId());
        BillFlow billFlow = this.transToBillFlow(request);
        this.addBillDetails(accountBase, intelligenceInvestBillBase, Arrays.asList(billFlow));
    }

    /**
     * 智能投记帐：底层标的投资成功
     * @param userId
     * @param orderId
     */
    @Transactional
    public void billIntelligenceInvestForRealProjectSuccess(String userId, String orderId) {
        //校验参数
        Assert.hasLength(userId, "userId不能为空");
        Assert.notNull(orderId, "orderId不能为空");
        OrderTypeEnum orderTypeEnum = OrderTypeEnum.ZNT_INVEST;
        log.info("billIntelligenceInvestForRealProjectSuccess start, userId:{}, orderId:{}, orderTypeEnum:{}", userId, orderId, orderTypeEnum);

        //查询账单流水明细
        BillStatusEnum newStatus = BillStatusEnum.SUCCESS;
        List<NiiwooBill> billFlowList = niiwooBillService.getByOrderIdAndType(userId, orderId, orderTypeEnum);
        log.info("通过订单ID查询出的账单流水是:{}", JSON.toJSONString(billFlowList));
        Assert.isTrue(billFlowList.size() == 1,"投资的账单流水不唯一，orderId:"+orderId);

        NiiwooBill billFlow = billFlowList.get(0);
        if (billFlow.getStatus() == newStatus.getValue()) {
            log.warn("当前流水状态已是成功，不需要再修改:{}", JSON.toJSONString(billFlow));
            return;
        }
        //修改账单流水明细状态
        log.info("智能投单笔投资已全部成功，修改账单流水的状态为成功:{}", JSON.toJSONString(billFlow));
        niiwooBillService.updateBillStatusByOrderId(userId, orderId, orderTypeEnum, newStatus);

        //修改账单状态(智能投：没有处理中的账单流水且总金额相等时，需要修改账单状态)
        UserBillBase userBillBase = userBillBaseMapperExt.queryUserBillBaseByIdForUpdate(userId, billFlow.getBillBaseId());
        log.info("billBase:{}",JSON.toJSONString(userBillBase));
        List<NiiwooBill> investBillFlowList = niiwooBillService.listNiiwooBillsByBaseId(userBillBase.getId(), userId);
        log.info("investBillFlowList:{}",JSON.toJSONString(investBillFlowList));
        long handlingBillFlowCount = investBillFlowList.stream().filter(e -> BillStatusEnum.HANDLING == BillStatusEnum.getEnumByCode(e.getStatus())).count();//状态为"处理中"的账单流水数量
        BigDecimal sumBillFlowAmount = investBillFlowList.stream().map(NiiwooBill::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);//账单流水总金额
        log.info("handlingBillFlowCount:{},sumBillFlowAmount:{} ",handlingBillFlowCount, sumBillFlowAmount);
        if(handlingBillFlowCount == 0 && userBillBase.getAmount().compareTo(sumBillFlowAmount) == 0){
            log.info("智能投全部投资已成功，修改账单Base的状态为成功:{}", JSON.toJSONString(billFlow));
            BillBase billBase = billBaseService.convertBillBase(userBillBase);
            billBaseService.updateBillBaseStatus(billBase, newStatus);
        }
    }

    /**
     * 智能投记帐：底层标的投资失败
     * @param userId
     * @param orderId
     */
    @Transactional
    public void billIntelligenceInvestForRealProjectFail(String userId, String orderId) {
        //校验参数
        Assert.hasLength(userId, "userId不能为空");
        Assert.notNull(orderId, "orderId不能为空");
        OrderTypeEnum orderTypeEnum = OrderTypeEnum.ZNT_INVEST;
        log.info("billIntelligenceInvestForFail start, orderId:{}, orderTypeEnum:{}, remark2:{}", orderId, orderTypeEnum);

        //查询账单流水明细
        Date nowDate = new Date();
        List<NiiwooBill> billFlowList = niiwooBillService.getByOrderIdAndType(userId, orderId, orderTypeEnum);
        log.info("通过订单ID查询出的账单流水是:{}", JSON.toJSONString(billFlowList));
        Assert.isTrue(billFlowList.size() == 1,"投资的账单流水不唯一，orderId:"+orderId);

        NiiwooBill oldBillFlow = billFlowList.get(0);
        if (oldBillFlow.getStatus() == BillStatusEnum.CLOSE.getValue()) {
            log.warn("当前流水状态为已关闭，不需要再修改:{}", JSON.toJSONString(oldBillFlow));
            return;
        }

        //关闭旧账单流水
        niiwooBillService.updateBillStatusByOrderId(userId, orderId, orderTypeEnum, BillStatusEnum.CLOSE);

        //新增资金回退账单Base和账单流水
        UserBillBase userBillBase = userBillBaseMapperExt.queryUserBillBaseByIdForUpdate(oldBillFlow.getUserId(), oldBillFlow.getBillBaseId());
        BillBase reverseBillBase = new BillBase();
        BeanUtils.copyProperties(userBillBase, reverseBillBase);
        reverseBillBase.setAmount(oldBillFlow.getAmount());
        reverseBillBase.setStatus(BillStatusEnum.SUCCESS.getValue());
        reverseBillBase.setOrderId("");
        reverseBillBase.setRequestNo("");
        reverseBillBase.setBackSourceBillId(userBillBase.getId());
        reverseBillBase.setCreateTime(new Date());
        reverseBillBase.setUpdateTime(new Date());
        reverseBillBase.setInoutType(InoutTypeEnum.IN_TYPE.getType());
        reverseBillBase.setBalanceAmount(accountBalanceLocalService.increAccountAmount(reverseBillBase.getAccountId(), reverseBillBase.getAmount()));
        billBaseService.addBillBase(reverseBillBase);

        NiiwooBill reverseBillFlow = new NiiwooBill();
        BeanUtils.copyProperties(oldBillFlow, reverseBillFlow);
        reverseBillFlow.setBillBaseId(reverseBillBase.getId());
        reverseBillFlow.setSecondSortCode(this.getReverseBillSecondSubject(oldBillFlow.getSecondSortCode()).getCode());
        reverseBillFlow.setBackTime(nowDate);
        reverseBillFlow.setStatus(BillStatusEnum.SUCCESS.getValue());
        niiwooBillService.addNiiwooBill(reverseBillBase, reverseBillFlow);

        //是否需要修改智能投产品包的账单Base
        BillBase billBase = billBaseService.convertBillBase(userBillBase);
        List<NiiwooBill> investBillFlowList = niiwooBillService.listNiiwooBillsByBaseId(billBase.getId(), userId);
        long handlingBillFlowCount = investBillFlowList.stream().filter(e -> BillStatusEnum.HANDLING.getValue() == e.getStatus()).count();//状态为"处理中"的账单流水数量
        BigDecimal sumBillFlowAmount = investBillFlowList.stream().map(NiiwooBill::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);//账单流水总金额
        log.info("handlingBillFlowCount:{},sumBillFlowAmount:{} ",handlingBillFlowCount, sumBillFlowAmount);
        if(handlingBillFlowCount == 0 && billBase.getAmount().compareTo(sumBillFlowAmount) == 0){
            log.info("智能投全部投资已失败，修改账单Base的状态为成功:{}", JSON.toJSONString(oldBillFlow));
            billBaseService.updateBillBaseStatus(billBase, BillStatusEnum.SUCCESS);
        }
    }

    /**
     * 取消智能投
     * @param userId
     * @param packageOrderId
     */
    @Transactional
    public void billIntelligenceInvestCancel(String userId, String packageOrderId){
        Assert.hasLength(userId,"userId不能为空");
        Assert.notNull(packageOrderId,"packageOrderId不能为空");
        BillBase billBase = billBaseService.getUserBillBaseByOrderId(userId,packageOrderId);
        UserBillBase userBillBase = userBillBaseMapperExt.queryUserBillBaseByIdForUpdate(userId, billBase.getId());
        log.info("userBillBase:{}", JSON.toJSONString(userBillBase));
        if(BillStatusEnum.HANDLING.getValue() == userBillBase.getStatus()){
            //查询智能投是否已匹配底层标
            List<NiiwooBill> niiwooBillList = niiwooBillService.listNiiwooBillsByBaseId(userBillBase.getId(), userId);
            if(niiwooBillList.size() > 0){
                throw new BizException("ACC12001");
            }

            //关闭账单
            billBaseService.updateBillBaseStatus(billBase, BillStatusEnum.CLOSE);

            //新增回退的账单Base
            BillBase reverseBillBase = new BillBase();
            BeanUtils.copyProperties(userBillBase, reverseBillBase);
            reverseBillBase.setInoutType(InoutTypeEnum.IN_TYPE.getType());
            reverseBillBase.setStatus(BillStatusEnum.CLOSE.getValue());
            reverseBillBase.setOrderId("");
            reverseBillBase.setRequestNo("");
            reverseBillBase.setBackSourceBillId(userBillBase.getId());
            reverseBillBase.setCreateTime(new Date());
            reverseBillBase.setUpdateTime(new Date());
            reverseBillBase.setBalanceAmount(accountBalanceLocalService.increAccountAmount(reverseBillBase.getAccountId(), reverseBillBase.getAmount()));
            billBaseService.addBillBase(reverseBillBase);
        }
    }

    /**
     * ============================================智能投记账方法区end==============================================
     */


    //通用记账方法
    private Long commonAddBillAndChangeBalance(AddBillAndChangeBalanceDTO billDTO) {
        Long accountId = billDTO.getAccountId();
        AccountBase accountBase = accountService.loadAccountById(accountId);

        //变更余额
        BigDecimal balanceAmount;
        if (InoutTypeEnum.IN_TYPE == billDTO.getInoutType()) {
            balanceAmount = accountBalanceLocalService.increAccountAmount(accountId, billDTO.getAmount());
        } else {
            balanceAmount = accountBalanceLocalService.decreAccountAmount(accountId, billDTO.getAmount());
        }

        //是否显示账单
        Byte isVisible = billDTO.getIsVisible();
        if (Objects.isNull(isVisible)) {
            isVisible = accountBase.getIsShowBill();
        }

        //查询标的信息
        ProjectDTO projectDTO = this.queryProjectInfo(billDTO.getProjectId());

        //智能投产品包id
        Long projectPackageId = billDTO.getProjectPackageId();
        if(Objects.isNull(projectPackageId) && Objects.nonNull(billDTO.getProjectId())){
            ProjectPackageResponseDTO projectPackageResponseDTO = intelligenceInvestDubboService.getProjectPackageByProjectId(billDTO.getProjectId());
            if(Objects.nonNull(projectPackageResponseDTO)){
                projectPackageId = projectPackageResponseDTO.getPackageId();
            }else {
                projectPackageId = billDTO.getProjectId();
            }
        }

        //组装AccountBillBase
        BillBase billBase = new BillBase();
        billBase.setAccountId(accountId);
        billBase.setUserId(accountBase.getUserId());
        billBase.setOrgId(accountBase.getOrgId());
        billBase.setAmount(billDTO.getAmount());
        billBase.setInoutType(billDTO.getInoutType().getType());
        billBase.setBalanceAmount(balanceAmount);
        billBase.setBillType(transToBillUserType(accountBase.getAccountRole()).getCode());
        billBase.setStatus(billDTO.getStatus().getValue());
        billBase.setRequestNo(billDTO.getRequestNo());
        billBase.setOrderId(String.valueOf(billDTO.getOrderId()));
        billBase.setOrderType(billDTO.getOrderType().getCode());
        billBase.setProjectId(billDTO.getProjectId());
        if (Objects.nonNull(billDTO.getProjectType())) {
            billBase.setProjectType(billDTO.getProjectType().getValue());
        }
        if(Objects.nonNull(projectDTO)){
            billBase.setLoanType(projectDTO.getLoanType());
        }
        billBase.setProjectPackageId(projectPackageId);
        billBase.setInvestId(billDTO.getInvestId());
        billBase.setGuaranteeId(billDTO.getGuaranteeId());
        billBase.setCurrentPeriod(billDTO.getCurrentPeriod());
        billBase.setTotalPeriods(billDTO.getTotalPeriod());
        billBase.setRemark(billDTO.getRemark());
        billBase.setIsVisible(isVisible);
        if (Objects.nonNull(billDTO.getChannelCode())) {
            billBase.setChannelCode(billDTO.getChannelCode().getChanelCode());
        }

        //新增BillBase
        billBaseService.addBillBase(billBase);

        //新增BillDetail
        List<BillFlow> billFlowList = billDTO.getAddBillDetailDTOList().stream().map(detailDTO -> {
            BillFlow billFlow = this.transToBillFlow(billDTO, detailDTO);
            return billFlow;
        }).collect(Collectors.toList());
        this.addBillDetails(accountBase, billBase, billFlowList);

        //返回
        return billBase.getId();
    }

    private BillFlow transToBillFlow(AddBillAndChangeBalanceDTO billDTO, AddBillDetailDTO detailDTO) {
        BillFlow billFlow = new BillFlow();
        billFlow.setSecondSubjectEnum(detailDTO.getBillSecondSubjectEnum());
        billFlow.setAmount(detailDTO.getAmount());
        billFlow.setRemark(detailDTO.getRemark());
        billFlow.setRemark2(detailDTO.getRemark2());
        if (Strings.isNullOrEmpty(detailDTO.getRelationUserId()) && !Strings.isNullOrEmpty(billDTO.getRelationUserId())) {
            billFlow.setRelationUserId(billDTO.getRelationUserId());
        } else {
            billFlow.setRelationUserId(detailDTO.getRelationUserId());
        }
        if (Objects.isNull(detailDTO.getRelationAccountId()) && Objects.nonNull(billDTO.getRelationAccountId())) {
            billFlow.setRelationAccountId(billDTO.getRelationAccountId());
        } else {
            billFlow.setRelationAccountId(detailDTO.getRelationAccountId());
        }


        billFlow.setProjectId(billDTO.getProjectId());
        billFlow.setProjectTypeEnum(billDTO.getProjectType());
        billFlow.setProjectTitle(billDTO.getProjectTitle());
        billFlow.setTotalPeriods(billDTO.getTotalPeriod());
        billFlow.setCurrentPeriod(billDTO.getCurrentPeriod());
        return billFlow;
    }


    private BillFlow transToBillFlow(BillIntelligenceInvestForMahchRequest request) {
        BillFlow billFlow = new BillFlow();
        billFlow.setSecondSubjectEnum(BillSecondSubjectEnum.USER_INVEST);
        billFlow.setAmount(request.getAmount());
        billFlow.setOrderId(request.getOrderId().toString());
        billFlow.setOrderTypeEnum(OrderTypeEnum.ZNT_INVEST);
        billFlow.setInvestId(Long.valueOf(billFlow.getOrderId()));
        billFlow.setProjectId(request.getProjectId());
        billFlow.setProjectTypeEnum(request.getProjectTypeEnum());
        billFlow.setProjectTitle(request.getProjectTitle());
        billFlow.setLoanTypeEnum(request.getLoanTypeEnum());
        billFlow.setRelationAccountId(request.getRelationAccountId());
        return billFlow;
    }

    private void addBillDetails(AccountBase accountBase, BillBase billBase, List<BillFlow> billFlowList) {
        if (CollectionUtils.isEmpty(billFlowList)) {
            return;
        }

        for (BillFlow billFlow : billFlowList) {
            //初始化属性
            String bankAccountNo = "";
            String bankCode = "";
            String bankName = "";
            BillSecondSubjectEnum secondSubjectEnum = billFlow.getSecondSubjectEnum();

            //获取银行卡信息
            boolean bankContain = BillSecondSubjectEnum.listBankImagEnum().contains(secondSubjectEnum.getCode());
            if (bankContain) {
                BankCard bankCard = bankCardService.loadBankCard(accountBase);
                BankType bankType = bankTypeMapper.selectByPrimaryKey(bankCard.getBankTypeId());
                Assert.notNull(bankType, "bankType不存在");

                bankAccountNo = bankCard.getBankAccountNo();
                bankCode = bankCard.getBankCode();
                bankName = bankType.getBankName();
            }

            //查询交易对象的账户信息(每条明细都不一样)
            Long relationAccountId = billFlow.getRelationAccountId();
            String relationUserId = billFlow.getRelationUserId();
            if (Strings.isNullOrEmpty(relationUserId) && Objects.nonNull(relationAccountId) && relationAccountId > 0) {
                relationUserId = accountService.loadAccountById(relationAccountId).getUserId();
            }

            //保存账单明细到对应的表中
            BillUserTypeEnum billUserType = BillUserTypeEnum.getEnumByCode(billBase.getBillType());
            Assert.notNull(billUserType, "二级科目中用户类型不能为空");
            if (BillUserTypeEnum.GENERAL == billUserType) {
                //普通用户和合作机构
                NiiwooBill niiwooBill = new NiiwooBill();
                niiwooBill.setAmount(billFlow.getAmount());
                niiwooBill.setSecondSortCode(secondSubjectEnum.getCode());
                niiwooBill.setOrderId(billFlow.getOrderId());
                if(Objects.nonNull(billFlow.getOrderTypeEnum())){
                    niiwooBill.setOrderType(billFlow.getOrderTypeEnum().getCode());
                }
                niiwooBill.setInvestId(billFlow.getInvestId());
                niiwooBill.setBankAccountNo(bankAccountNo);
                niiwooBill.setBankCode(bankCode);
                niiwooBill.setBankName(bankName);
                niiwooBill.setProjectId(billFlow.getProjectId());
                if(Objects.nonNull(billFlow.getProjectTypeEnum())){
                    niiwooBill.setProjectType(billFlow.getProjectTypeEnum().getValue());
                }
                niiwooBill.setProjectTitle(billFlow.getProjectTitle());
                niiwooBill.setTotalPeriods(billFlow.getTotalPeriods());
                niiwooBill.setCurrentPeriod(billFlow.getCurrentPeriod());
                niiwooBill.setRelationUserId(relationUserId);
                niiwooBill.setRelationAccountId(relationAccountId);
                niiwooBill.setChannelCode(billBase.getChannelCode());
                niiwooBill.setRemark(this.buildBillRemark(billFlow));
                niiwooBill.setRemark2(billFlow.getRemark2());
                niiwooBill.setIsVisible(billBase.getIsVisible());
                niiwooBillService.addNiiwooBill(billBase, niiwooBill);
            } else if (BillUserTypeEnum.GUARANTEE_AGENCY == billUserType || BillUserTypeEnum.COOPERATE_AGENCY == billUserType) {
                //担保机构、合作机构(上存管前的合作机构数据放在用户账单表中,billType=2)
                NiiwooBillGuarantee niiwooBillGuarantee = new NiiwooBillGuarantee();
                niiwooBillGuarantee.setAmount(billFlow.getAmount());
                niiwooBillGuarantee.setSecondSortCode(secondSubjectEnum.getCode());
                niiwooBillGuarantee.setBankCode(bankCode);
                niiwooBillGuarantee.setProjectId(billFlow.getProjectId());
                if(Objects.nonNull(billFlow.getProjectTypeEnum())){
                    niiwooBillGuarantee.setProjectType(billFlow.getProjectTypeEnum().getValue());
                }
                niiwooBillGuarantee.setRelationUserId(relationUserId);
                niiwooBillGuarantee.setRelationAccountId(relationAccountId);
                niiwooBillGuarantee.setChannelCode(billBase.getChannelCode());
                niiwooBillGuarantee.setRemark(this.buildBillRemark(billFlow));
                niiwooBillGuaranteeService.addNiiwooBillGuarantee(billBase, niiwooBillGuarantee);
            } else if (BillUserTypeEnum.PLATFORM == billUserType) {
                //平台
                NiiwooBillPlatform niiwooBillPlatform = new NiiwooBillPlatform();
                niiwooBillPlatform.setAmount(billFlow.getAmount());
                niiwooBillPlatform.setSecondSortCode(secondSubjectEnum.getCode());
                niiwooBillPlatform.setProjectId(billFlow.getProjectId());
                if(Objects.nonNull(billFlow.getProjectTypeEnum())){
                    niiwooBillPlatform.setProjectType(billFlow.getProjectTypeEnum().getValue());
                }
                niiwooBillPlatform.setCurrentPeriod(billFlow.getCurrentPeriod());
                niiwooBillPlatform.setRelationUserId(relationUserId);
                niiwooBillPlatform.setRelationAccountId(relationAccountId);
                niiwooBillPlatform.setChannelCode(billBase.getChannelCode());
                niiwooBillPlatform.setRemark(this.buildBillRemark(billFlow));
                niiwooBillPlateformService.addNiiwooBillPlatform(billBase, niiwooBillPlatform);
            }
        }
    }

    //验证参数传的是否正确
    private void verifyParamAddBillAndChangeBalance(AddBillAndChangeBalanceDTO billDTO) {
        Assert.notNull(billDTO.getAccountId(), "accountId不能为空");
        Assert.notNull(billDTO.getAmount(), "amount不能为空");
        Assert.notNull(billDTO.getInoutType(), "inoutType");
        Assert.hasLength(billDTO.getRequestNo(), "requestNo不能为空");
        Assert.notNull(billDTO.getOrderId(), "orderId不能为空");
        Assert.notNull(billDTO.getOrderType(), "orderType不能为空");
        Assert.notNull(billDTO.getChannelCode(), "channelCode不能为空");
        Assert.notNull(billDTO.getStatus(), "status不能为空");
        Assert.notEmpty(billDTO.getAddBillDetailDTOList(), "addBillDetailDTOList不能为空");

        if (Objects.nonNull(billDTO.getProjectId())) {
            Assert.notNull(billDTO.getProjectType(), "projectType不能为空");
            Assert.hasLength(billDTO.getProjectTitle(), "projectTitle不能为空");
        }

        if (billDTO.getAccountId() <= 0) {
            Assert.notNull(billDTO.getUserId(), "userId不能为空");
        }

        List<AddBillDetailDTO> addBillDetailDTOS = billDTO.getAddBillDetailDTOList();
        if (!CollectionUtils.isEmpty(addBillDetailDTOS)) {
            addBillDetailDTOS.forEach(billDetail -> {
                Assert.notNull(billDetail.getAmount(), "金额不能为空");
                Assert.notNull(billDetail.getBillSecondSubjectEnum(), "二级科目枚举不能为空");
                if (Objects.nonNull(billDTO.getProjectId())) {
                    BillFirstSubjectEnum firstEnum = billDetail.getBillSecondSubjectEnum().getFirstSubject();
                    List<String> currentPeriodList = BillSecondSubjectEnum.listCurrentPeriodEnum();
                    boolean containPeriod = currentPeriodList.contains(firstEnum.getCode());
                    if (containPeriod) {
                        Assert.notNull(billDTO.getCurrentPeriod(), "当期期数不能为空");
                        Assert.notNull(billDTO.getTotalPeriod(), "总期数不能为空");
                    }
                }
            });
        }
    }

    //验证参数传的是否正确
    private void verifyParamHandAddBillAndChangeBalance(HandAddBillAndChangeBalanceDTO billDTO) {
        Assert.notNull(billDTO.getAccountId(), "accountId不能为空");
        Assert.notNull(billDTO.getAmount(), "amount不能为空");
        Assert.notNull(billDTO.getInoutType(), "inoutType");
        Assert.notNull(billDTO.getChannelCode(), "channelCode不能为空");
        Assert.notNull(billDTO.getStatus(), "status不能为空");
        Assert.notNull(billDTO.getBillSecondSubjectEnum(), "二级科目枚举不能为空");
    }


    public BillUserTypeEnum transToBillUserType(Byte accountRole) {
        UserRoleEnum userRoleEnum = UserRoleEnum.enumOf(accountRole);
        switch (userRoleEnum) {
            case GUARANTEECORP:
                return BillUserTypeEnum.GUARANTEE_AGENCY;
            case INVESTOR:
                return BillUserTypeEnum.GENERAL;
            case BORROWERS:
                return BillUserTypeEnum.GENERAL;
            case COLLABORATOR:
                return BillUserTypeEnum.COOPERATE_AGENCY;
            case PLATFORM_MARKETING:
                return BillUserTypeEnum.PLATFORM;
            case PLATFORM_PROFIT:
                return BillUserTypeEnum.PLATFORM;
            case PLATFORM_INCOME:
                return BillUserTypeEnum.PLATFORM;
            case PLATFORM_INTEREST:
                return BillUserTypeEnum.PLATFORM;
            case PLATFORM_ALTERNATIVE_RECHARGE:
                return BillUserTypeEnum.PLATFORM;
            case PLATFORM_FUNDS_TRANSFER:
                return BillUserTypeEnum.PLATFORM;
            case PLATFORM_URGENT:
                return BillUserTypeEnum.PLATFORM;
            default:
                throw new RuntimeException("没有对应的账单用户类型:" + accountRole);
        }
    }


    //获取回滚的账单二级科目
    private BillSecondSubjectEnum getReverseBillSecondSubject(String billSecondSubject) {
        BillSecondSubjectEnum billSecondSubjectEnum = BillSecondSubjectEnum.getEnumByCode(billSecondSubject);
        switch (billSecondSubjectEnum) {
            /**============还款==============**/
            //用户还款
            case USER_REPAYMENT_CORPUS:
                return BillSecondSubjectEnum.USER_REPAYMENT_BACK_CORPUS;//还款本金
            case USER_REPAYMENT_INTEREST:
                return BillSecondSubjectEnum.USER_REPAYMENT_BACK_INTEREST; //还款利息
            case USER_REPAYMENT_PENALTY:
                return BillSecondSubjectEnum.USER_REPAYMENT_BACK_OVERDUE_FINE; //罚息
            case USER_REPAYMENT_MANAGER_FEE:
                return BillSecondSubjectEnum.USER_REPAYMENT_BACK_PLATFORM_SERVICE_FEE; //平台管理费
            case USER_REPAYMENT_GUARANTEE_FEE:
                return BillSecondSubjectEnum.USER_REPAYMENT_BACK_GUARANTEE_SERVICE_FEE; //担保服务费
            //担保机构/合作机构垫付
            case GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_PRINCIPAL_SPEED_BORROW:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_BACK_CORPUS;//垫付本金
            case GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_INTEREST:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_BACK_INTEREST;//垫付利息
            case GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_PENALTY:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_BACK_OVERDUE_FINE;//垫付罚息
            case GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_MANAGE_FEE:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_BACK_PLATFORM_SERVICE_FEE;//垫付平台管理费
            case GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_GUARANTEE_FEE:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_BACK_GUARANTEE_SERVICE_FEE;//垫付担保服务费
            /**============提现==============**/
            //用户提现
            case USER_WITHDRAW:
                return BillSecondSubjectEnum.USER_WITHDRAW_BACK; //用户提现金额
            case USER_WITHDRAW_SERVICE_FEE:
                return BillSecondSubjectEnum.USER_WITHDRAW_BACK_SERVICE_FEE; //用户付出的提现服务费
            //担保机构提现
            case GUARANTEE_AGENCY_WITHDRAW_AMOUNT:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_WITHDRAW_RETURN_AMOUNT; //担保机构提现金额
            case GUARANTEE_AGENCY_WITHDRAW_FEE:
                return BillSecondSubjectEnum.GUARANTEE_AGENCY_WITHDRAW_RETURN_FEE; //担保机构付出的提现服务费
            //合作机构提现
            case COOPERATE_AGENCY_WITHDRAW_AMOUNT:
                return BillSecondSubjectEnum.COOPERATE_AGENCY_WITHDRAW_RETURN_AMOUNT; //合作机构提现金额
            case COOPERATE_AGENCY_WITHDRAW_FEE:
                return BillSecondSubjectEnum.COOPERATE_AGENCY_WITHDRAW_RETURN_FEE; //合作机构付出的提现服务费
            //平台提现
            case PLATFORM_WITHDRAW_AMOUNT:
                return BillSecondSubjectEnum.PLATFORM_WITHDRAW_BACK; //平台提现金额
            case PLATFORM_SERVICE_FEE_IN_WITHDRAW:
                return BillSecondSubjectEnum.PLATFORM_SERVICE_FEE_OUT_WITHDRAW; //平台收取的提现服务费
            /**============投资==============**/
            case USER_INVEST:
                return BillSecondSubjectEnum.USER_INVEST_BACK; //投资金额
            default:
                throw new RuntimeException("没有对应的回退账单科目:" + billSecondSubject);
        }
    }


    //组装账单的remark
    private String buildBillRemark(BillFlow billFlow) {
        String remark = billFlow.getRemark();
        if (Strings.isNullOrEmpty(remark)) {
            BillSecondSubjectEnum billSecondSubjectEnum = billFlow.getSecondSubjectEnum();
            remark = billSecondSubjectEnum.getDesc();
            if (BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_PRINCIPAL_SPEED_BORROW == billSecondSubjectEnum
                    || BillSecondSubjectEnum.GUARANTEE_AGENCY_ADVANCE_GUARANTEE_OUT_INTEREST == billSecondSubjectEnum) {
                if (Objects.nonNull(billFlow.getCurrentPeriod()) && Objects.nonNull(billFlow.getTotalPeriods())) {
                    remark = String.format(remark, billFlow.getCurrentPeriod() + "/" + billFlow.getTotalPeriods());
                } else {
                    remark = StringUtils.replace(remark, "第%s期", "");
                }
            }
        }

        return remark;
    }

    //查询项目信息
    private ProjectDTO queryProjectInfo(Long projectId){
        if(Objects.nonNull(projectId)){
            return projectDubboService.selectProjectById(projectId);
        }
        return null;
    }

}
